#ifdef _FULLPROGRAM

#include "klexer.h"
#include "kerror.h"
#include "kutils.h"

#include <cstring>
#include <cctype>
#include <sstream>

const char *ObjectIdents[] =
{
	"io", "var", "stack", "tape", "ctrl"
};

const char *MethodIdents[] =
{
	"in", "out",
	"decl", "del", "set",
	"push", "pop", "peek", "swap", "clear",
	"read", "write", "next", "prev", "move",
	"goto", "call", "return", "end"
};

const char *FuncIdents[] =
{
	"abs", "sqrt",
	"sin", "cos", "tan", "asin", "acos", "atan",
	"chr", "asc", "len", "left", "mid", "right",
	"iif", "_tape", "_stack", "_pop", "_peek"
};

void Lexer::Scan(const char* src)
{
	this->CleanUp();

	this->index = 0;
	this->line = 1;

	int len = strlen(src) + 1;
	this->source = new char[len];
#ifdef _MSC_VER
	strcpy_s(this->source, len, src);
#else
	strncpy(this->source, src, len);
#endif

	oldtok.canPrecedeUnary = true;
	unsigned char c;

	while (source[index])
	{
		this->IgnoreWhite();

		c = source[index];

		tok.line = this->line;
		tok.v_string.clear();

		tok.precedence = -1;
		tok.isBinary = true;
		tok.canPrecedeUnary = true;

		if (isalpha(c) || c == '_')
		{
			this->ScanSymbol();
		}
		else if (isdigit(c))
		{
			this->ScanNumber();
		}
		else if (c == '"')
		{
			this->ScanString();
		}
		else
		{
			switch (c)
			{
			case '+':
				if (oldtok.canPrecedeUnary)
				{
					tok.type = TOK_UPLUS;
					tok.precedence = 8;
					tok.isBinary = false;
				}
				else
				{
					tok.type = TOK_ADD;
					tok.precedence = 6;
				}
				break;
			case '-':
				if (oldtok.canPrecedeUnary)
				{
					tok.type = TOK_UMINUS;
					tok.precedence = 8;
					tok.isBinary = false;
				}
				else
				{
					tok.type = TOK_SUB;
					tok.precedence = 6;
				}
				break;
			case '*':
				tok.type = TOK_MUL;
				tok.precedence = 7;
				break;
			case '/':
				tok.type = TOK_DIV;
				tok.precedence = 7;
				break;
			case '\\':
				tok.type = TOK_IDIV;
				tok.precedence = 7;
				break;
			case '%':
				tok.type = TOK_MOD;
				tok.precedence = 7;
				break;
			case '^':
				tok.type = TOK_POW;
				tok.precedence = 9;
				break;
			case '=':
				tok.type = TOK_EQU;
				tok.precedence = 3;
				break;
			case '<':
				if (source[index+1] == '=')
				{
					tok.type = TOK_LTE;
					tok.precedence = 4;
					++index;
				}
				else if (source[index+1] == '>')
				{
					tok.type = TOK_NEQ;
					tok.precedence = 3;
					++index;
				}
				else
				{
					tok.type = TOK_LT;
					tok.precedence = 4;
				}
				break;
			case '>':
				if (source[index+1] == '=')
				{
					tok.type = TOK_GTE;
					++index;
				}
				else
				{
					tok.type = TOK_GT;
				}
				tok.precedence = 4;
				break;
			case '&':
				tok.type = TOK_CAT;
				tok.precedence = 5;
				break;
			case '@':
				tok.type = TOK_CINT;
				tok.precedence = 10;
				break;
			case '#':
				tok.type = TOK_CREAL;
				tok.precedence = 10;
				break;
			case ',':
				tok.type = TOK_COMMA;
				tok.isBinary = false;
				break;
			case ':':
				tok.type = TOK_COLON;
				tok.isBinary = false;
				break;
			case '.':
				tok.type = TOK_SELECTOR;
				tok.isBinary = false;
				break;
			case '(':
				tok.type = TOK_LPAR;
				tok.isBinary = false;
				break;
			case ')':
				tok.type = TOK_RPAR;
				tok.isBinary = false;
				tok.canPrecedeUnary = false;
				break;
			case '\n':
				tok.type = TOK_EOS;
				tok.isBinary = false;
				++(this->line);
				break;
			case ';':
				tok.type = TOK_EOS;
				tok.isBinary = false;
				++(this->line);
				while (source[index] != '\0'
				&& source[index] != '\n')
					++index;
				break;
			default:
				throw KLexicalError(KLE_INVALID_CHAR, this->line);
			} //switch

			++index;

		} //if

		tokenList.push_back(tok);
		oldtok = tok;

	} //while

	tok.type = TOK_NULL;
	tok.line = this->line;
	tokenList.push_back(tok);
}

k_uint Lexer::GetStringPoolSize() const
{
	return this->strPoolSize;
}

std::vector<Token> Lexer::GetTokenList() const
{
	return this->tokenList;
}

void Lexer::ScanSymbol()
{
	std::stringbuf sb;
	tmpstr.clear();

	while (isalnum(source[index]) || source[index] == '_')
	{
		sb.sputc(source[index]);
		++index;
	}

	tmpstr = sb.str();

	int i;
	if ((i = this->GetObjectIndex()) != -1)
	{
		tok.type = TOK_OBJECT;
		tok.v_int = i;
		tok.isBinary = false;
	}
	else if ((i = this->GetMethodIndex()) != -1)
	{
		tok.type = TOK_METHOD;
		tok.v_int = i;
		tok.isBinary = false;
	}
	else if ((i = this->GetFuncIndex()) != -1)
	{
		tok.type = TOK_FUNC;
		tok.v_int = i;
		tok.isBinary = false;
		tok.precedence = 11;
	}
	else
	{
		if (my_strequ(tmpstr.c_str(), "and"))
		{
			tok.type = TOK_AND;
			tok.precedence = 1;
		}
		else if (my_strequ(tmpstr.c_str(), "or"))
		{
			tok.type = TOK_OR;
			tok.precedence  = 0;
		}
		else if (my_strequ(tmpstr.c_str(), "xor"))
		{
			tok.type = TOK_XOR;
			tok.precedence = 2;
		}
		else if (my_strequ(tmpstr.c_str(), "not"))
		{
			tok.type = TOK_NOT;
			tok.precedence = 8;
			tok.isBinary = false;
		}
		else if (my_strequ(tmpstr.c_str(), "if"))
		{
			tok.type = TOK_IF;
			tok.isBinary = false;
		}
		else if (my_strequ(tmpstr.c_str(), "at"))
		{
			tok.type = TOK_AT;
			tok.isBinary = false;
		}
		else if (my_strequ(tmpstr.c_str(), "int"))
		{
			tok.type = TOK_INT;
			tok.isBinary = false;
		}
		else if (my_strequ(tmpstr.c_str(), "real"))
		{
			tok.type = TOK_REAL;
			tok.isBinary = false;
		}
		else if (my_strequ(tmpstr.c_str(), "string"))
		{
			tok.type = TOK_STRING;
			tok.isBinary = false;
		}
		else
		{
			tok.type = TOK_VAR;
			tok.v_string = tmpstr;
			tok.canPrecedeUnary = false;
			tok.isBinary = false;
		}
	}
}

void Lexer::ScanNumber()
{
	std::stringbuf sb;
	tmpstr.clear();

	bool isInt = true;

	while (isdigit(source[index]))
	{
		sb.sputc(source[index]);
		++index;
	}

	if (source[index] == '.')
	{
		isInt = false;
		++index;

		sb.sputc('.');
		while (isdigit(source[index]))
		{
			sb.sputc(source[index]);
			++index;
		}
	}

	tmpstr = sb.str();

	std::istringstream is(tmpstr);
	if (isInt)
	{
		tok.type = TOK_LITINT;
		if (!(is >> tok.v_int))
			tok.v_int = (k_int)0;
	}
	else
	{
		tok.type = TOK_LITREAL;
		if (!(is >> tok.v_real))
			tok.v_real = (k_real)0;
	}
	tok.canPrecedeUnary = false;
	tok.isBinary = false;
}

void Lexer::ScanString()
{
	std::stringbuf sb;
	tmpstr.clear();

	unsigned char es;

	++index; //skip quote
	while (source[index] != '"')
	{
		if (source[index] == '\0')
			throw KLexicalError(KLE_EXPECT_QUOTE, this->line);

		if (source[index] == '\\')
		{
			++index;
			es = source[index];
			switch (es)
			{
			case 'n':
				sb.sputc('\n');
				break;
			case 'r':
				sb.sputc('\r');
				break;
			case 't':
				sb.sputc('\t');
				break;
			case '\\':
				sb.sputc('\\');
				break;
			case '"':
				sb.sputc('"');
				break;
			default:
				sb.sputc('\\');
				sb.sputc(es);
			}
		}
		else
		{
			sb.sputc(source[index]);
		}

		++index;
	} //while

	++index; //skip quote

	tmpstr = sb.str();
	this->strPoolSize += tmpstr.length() + 1;

	tok.type = TOK_LITSTRING;
	tok.v_string = tmpstr;
	tok.canPrecedeUnary = false;
	tok.isBinary = false;
}

int Lexer::GetObjectIndex()
{
	for (int i = 0; i < OBJECT_COUNT; ++i)
		if (my_strequ(tmpstr.c_str(), ObjectIdents[i]))
			return i;
	return -1;
}

int Lexer::GetMethodIndex()
{
	for (int i = 0; i < METHOD_COUNT; ++i)
		if (my_strequ(tmpstr.c_str(), MethodIdents[i]))
			return i;
	return -1;
}

int Lexer::GetFuncIndex()
{
	for (int i = 0; i < FUNC_COUNT; ++i)
		if (my_strequ(tmpstr.c_str(), FuncIdents[i]))
			return i;
	return -1;
}

void Lexer::IgnoreWhite()
{
	while (source[index] == ' '
	|| source[index] == '\t'
	|| source[index] == '\r')
		++index;
}

void Lexer::CleanUp()
{
	delete[] this->source;
	this->source = 0;

	this->tokenList.clear();
	this->strPoolSize = 0;
	this->tmpstr.clear();
}

#endif //_FULLPROGRAM
